package com.tianji.remark.service.impl;

import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.utils.ObjectUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.remark.domain.dto.LikedTimesDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.tianji.common.constants.MqConstants.Exchange.LIKE_RECORD_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE;
import static com.tianji.remark.constants.RedisConstants.LIKE_BIZ_KEY_PREFIX;
import static com.tianji.remark.constants.RedisConstants.LIKE_TIMES_KEY_PREFIX;

/**
 * <p>
 * 点赞记录表 服务实现类
 * </p>
 *
 * @author author
 * @since 2024-11-15
 */
@Service
@RequiredArgsConstructor
public class LikedRecordServiceImpl extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {
    private final RabbitMqHelper mqHelper;
    private final StringRedisTemplate redisTemplate;

    @Override
    public void likeOrCancel(LikeRecordFormDTO likeRecordFormDTO) {
        //在redis中进行增加或者删除
        //1.拼接key
        String key = LIKE_BIZ_KEY_PREFIX + likeRecordFormDTO.getBizId();
        //2.判断类型
        boolean liked = likeRecordFormDTO.getLiked() ? likeId(likeRecordFormDTO, key) : cancelId(likeRecordFormDTO, key);
        if (!liked) {
            //失败直接结束
            return;
        }
        //3.点赞成功，统计点赞数量
        Long size = redisTemplate.opsForSet().size(key);
        if (ObjectUtils.isEmpty(size)) {
            return;
        }
        //4.存进zset
        //4.1拼接key
        String zsetKey = LIKE_TIMES_KEY_PREFIX + likeRecordFormDTO.getBizType();
        Boolean add = redisTemplate.opsForZSet().add(zsetKey, likeRecordFormDTO.getBizId().toString(), size);
        if (ObjectUtils.isEmpty(add)) {
            //失败应该报错
            throw new RuntimeException("点赞失败");
        }
    }

    /**
     * 实现批量查询点赞状态
     *
     * @param bizIds
     * @return
     */
    @Override
    public Set<Long> isLiked(List<Long> bizIds) {
        //1.获取用户id
        Long userId = UserContext.getUser();
        //2.收集所有已经点赞的业务id
        HashSet<Long> ids = new HashSet<>(bizIds.size());

        //2.1用Redis的管道方法批量查询
        List<Object> result = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            //1.转换connection
            StringRedisConnection connect = (StringRedisConnection) connection;
            //2.批量查询
            for (Long bizId : bizIds) {
                connect.sIsMember(LIKE_BIZ_KEY_PREFIX + bizId, userId.toString());
            }
            return null;
        });

        //通过stream流收集所有为结果true的业务id
        Set<Long> collect = IntStream.range(0, result.size())
                .filter(i -> (Boolean) result.get(i))
                .mapToObj(bizIds::get)
                .collect(Collectors.toSet());
        //返回结果
        return collect;
    }


    /**
     * 定时任务
     *
     * @param bizType
     * @param maxBizSize
     */
    @Override
    public void sendMessage(String bizType, int maxBizSize) {
        //1.读取Redis中的点赞总数
        //1.1拼接Key
        String key = LIKE_TIMES_KEY_PREFIX + bizType;
        //1.2.拿取并移出
        Set<ZSetOperations.TypedTuple<String>> typedTuples = redisTemplate.opsForZSet().popMin(key, maxBizSize);

        while (ObjectUtils.isNotEmpty(typedTuples)) {
            //2.拿取内容放入集合中发送给mq
            List<LikedTimesDTO> list = new ArrayList<>(typedTuples.size());
            for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
                String bizId = typedTuple.getValue();
                Double likedTimes = typedTuple.getScore();
                //如果为空要跳过
                if (ObjectUtils.isEmpty(bizId)) {
                    continue;
                }
                list.add(new LikedTimesDTO(Long.valueOf(bizId), likedTimes.intValue()));
            }

            //3.发送消息
            if (ObjectUtils.isNotEmpty(list)) {
                mqHelper.send(LIKE_RECORD_EXCHANGE, StringUtils.format(LIKED_TIMES_KEY_TEMPLATE, bizType), list);
            }
            //4.继续下一轮
            typedTuples = redisTemplate.opsForZSet().popMin(key, maxBizSize);
        }
    }

    /**
     * 取消点赞逻辑
     *
     * @param likeRecordFormDTO
     * @param key
     * @return
     */
    private boolean cancelId(LikeRecordFormDTO likeRecordFormDTO, String key) {
        //redis中取消点赞
        //1.直接删除记录
        Long flag = redisTemplate.opsForSet().remove(key, UserContext.getUser().toString());
        if (ObjectUtils.isEmpty(flag)) {
            //失败应该报错
            throw new RuntimeException("取消点赞失败");
        }
        if (flag > 0) {
            return true;
        }
        return false;
    }

    /**
     * 点赞逻辑
     *
     * @param likeRecordFormDTO
     * @param key
     * @return
     */
    private boolean likeId(LikeRecordFormDTO likeRecordFormDTO, String key) {
        //redis中点赞
        //用set存储点赞记录
        //1.直接新增记录
        Long flag = redisTemplate.opsForSet().add(key, UserContext.getUser().toString());
        if (ObjectUtils.isEmpty(flag)) {
            //失败应该报错
            throw new RuntimeException("点赞失败");
        }
        if (flag > 0) {
            return true;
        }
        return false;
    }


}
